#include "vsf.h"

#if defined(BCMWIFI_SDIO) || defined(BCMWIFI_SPI)

// F2 handler thread
enum bcm_bus_EVT_t
{
	// VSFSM_EVT_USER_LOCAL + 0 is used for critical sections
	BCMBUS_EVT_WAKEUP = VSFSM_EVT_USER_LOCAL + 1,
	BCMBUS_EVT_INTERRUPT = VSFSM_EVT_USER_LOCAL + 2,
	BCMBUS_EVT_SELFWAKEUP = VSFSM_EVT_USER_LOCAL + 3,
};

vsf_err_t bcm_bus_construct(struct bcm_bus_t *bcm_bus)
{
	bcm_bus->is_up = 0;
	bcm_bus->init_pt.user_data = bcm_bus;
	bcm_bus->bufacc_pt.user_data = bcm_bus;
	bcm_bus->download_firmware_pt.user_data = bcm_bus;
	bcm_bus->core_ctrl_pt.user_data = bcm_bus;
	bcm_bus->regacc_pt.user_data = bcm_bus;
	bcm_bus->port_init_pt.user_data = bcm_bus;
	bcm_bus->download_image_pt.user_data = bcm_bus;
	bcm_bus->download_firmware_pt.user_data = bcm_bus;
	bcm_bus->f2_pt.user_data = bcm_bus;

	bcm_bus->bufacc_buffer.size = sizeof(bcm_bus->bufacc_mem);
	bcm_bus->regacc_buffer.size = sizeof(bcm_bus->regacc_mem);

	vsfsm_crit_init(&bcm_bus->crit_reg, VSFSM_EVT_USER_LOCAL);
	vsfsm_crit_init(&bcm_bus->crit_buffer, VSFSM_EVT_USER_LOCAL);
	vsfsm_crit_init(&bcm_bus->crit_init, VSFSM_EVT_USER_LOCAL);
	return VSFERR_NONE;
}

vsf_err_t bcm_bus_fini(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;

	vsfhal_gpio_clear(bcm_bus->port.rst_port, 1 << bcm_bus->port.rst_pin);
	bcm_bus->is_up = 0;
	return VSFERR_NONE;
}

static vsf_err_t bcm_bus_disable_device_core(struct vsfsm_pt_t *pt,
												vsfsm_evt_t evt, uint32_t base)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err;

	vsfsm_pt_begin(pt);

	bcm_bus->core_ctrl_pt.sm = pt->sm;
	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
			base + BCM_COREREG_AI_RESETCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
			base + BCM_COREREG_AI_RESETCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->core_ctrl_pt, evt,
							base + BCM_COREREG_AI_IOCTRL, 1, 0);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
				base + BCM_COREREG_AI_IOCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	vsfsm_pt_delay(pt, 2);

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->core_ctrl_pt, evt,
							base + BCM_COREREG_AI_RESETCTRL, 1,
							BCM_RESETCTRL_RESET);
	if (err != 0) return err;

	vsfsm_pt_delay(pt, 2);

	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

static vsf_err_t bcm_bus_reset_device_core(struct vsfsm_pt_t *pt,
											vsfsm_evt_t evt, uint32_t base)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err;

	vsfsm_pt_begin(pt);

	bcm_bus->core_ctrl_pt.sm = pt->sm;
	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->core_ctrl_pt, evt,
							base + BCM_COREREG_AI_IOCTRL, 1,
							BCM_IOCTRL_SICF_CLOCK_EN | BCM_IOCTRL_SICF_FGC);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
				base + BCM_COREREG_AI_IOCTRL, 1, (uint8_t*)&bcm_bus->tmpreg);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->core_ctrl_pt, evt,
							base + BCM_COREREG_AI_RESETCTRL, 1, 0);
	if (err != 0) return err;

	vsfsm_pt_delay(pt, 2);

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->core_ctrl_pt, evt,
							base + BCM_COREREG_AI_IOCTRL, 1,
							BCM_IOCTRL_SICF_CLOCK_EN);
	if (err != 0) return err;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
				base + BCM_COREREG_AI_IOCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	vsfsm_pt_delay(pt, 2);

	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

static vsf_err_t bcm_bus_device_core_isup(struct vsfsm_pt_t *pt,
									vsfsm_evt_t evt, uint32_t base, bool *up)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err;

	vsfsm_pt_begin(pt);

	bcm_bus->core_ctrl_pt.sm = pt->sm;

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
				base + BCM_COREREG_AI_IOCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	if ((bcm_bus->tmpreg & (BCM_IOCTRL_SICF_CLOCK_EN | BCM_IOCTRL_SICF_FGC)) !=
		BCM_IOCTRL_SICF_CLOCK_EN)
	{
		*up = false;
		return VSFERR_NONE;
	}

	bcm_bus->core_ctrl_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->core_ctrl_pt, evt,
			base + BCM_COREREG_AI_RESETCTRL, 1, (uint8_t *)&bcm_bus->tmpreg);
	if (err != 0) return err;

	if ((bcm_bus->tmpreg & BCM_RESETCTRL_RESET) != 0)
	{
		*up = false;
		return VSFERR_NONE;
	}

	*up = true;
	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

static vsf_err_t bcm_bus_download_image(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
								uint32_t addr, uint8_t *image, uint32_t size)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err;

	vsfsm_pt_begin(pt);

	while (bcm_bus->download_progress < size)
	{
		bcm_bus->download_image_pt.sm = pt->sm;
		bcm_bus->download_image_pt.state = 0;

		bcm_bus->download_image_buffer.buffer = bcm_bus->download_image_mem;
		memcpy(bcm_bus->download_image_buffer.buffer,
				image + bcm_bus->download_progress, 64);

		bcm_bus->download_image_pt.state = 0;
		vsfsm_pt_entry(pt);
		bcm_bus->download_image_buffer.size =
					(min(64, size - bcm_bus->download_progress) + 63) & ~63;
		err = bcm_bus_transact(&bcm_bus->download_image_pt, evt,
						BCM_CMD_WRITE, BCM_FUNC_BACKPLANE,
						(addr + bcm_bus->download_progress) | 0x80000000,
						bcm_bus->download_image_buffer.size,
						&bcm_bus->download_image_buffer);
		if (err != 0) return err;
		
#if IFS_SDIO_EN
		if (BCM_BUS_TYPE_SDIO == bcm_bus->type)
		{
			memcpy(bcm_bus->verify_image_mem,
					bcm_bus->download_image_buffer.buffer, 64);
			memset(bcm_bus->download_image_buffer.buffer, 0, 64);

			bcm_bus->download_image_pt.state = 0;
			vsfsm_pt_entry(pt);
			err = bcm_bus_transact(&bcm_bus->download_image_pt, evt,
							BCM_CMD_READ, BCM_FUNC_BACKPLANE,
							(addr + bcm_bus->download_progress) | 0x80000000,
							bcm_bus->download_image_buffer.size,
							&bcm_bus->download_image_buffer);
			if (err != 0) return err;

			if (memcmp(bcm_bus->verify_image_mem,
						bcm_bus->download_image_buffer.buffer, 64) != 0)
			{
				return VSFERR_FAIL;
			}
		}
#endif // IFS_SDIO_EN

		bcm_bus->download_progress += bcm_bus->download_image_buffer.size;
	}

	vsfsm_pt_end(pt);

	return VSFERR_NONE;
}

#include "../bcm_image_43362A2.h"
static vsf_err_t bcm_bus_download_firmware_nvram(struct vsfsm_pt_t *pt,
													vsfsm_evt_t evt)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err;
	bool up;
	uint32_t reg;

	vsfsm_pt_begin(pt);

	bcm_bus->download_firmware_pt.sm = pt->sm;
	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_disable_device_core(&bcm_bus->download_firmware_pt, evt,
										BCM_CORE_ARM_BASE);
	if (err != 0) return err;

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_disable_device_core(&bcm_bus->download_firmware_pt, evt,
										BCM_CORE_SOCRAM_BASE);
	if (err != 0) return err;

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_reset_device_core(&bcm_bus->download_firmware_pt, evt,
										BCM_CORE_SOCRAM_BASE);
	if (err != 0) return err;

	// download firmware
	bcm_bus->download_firmware_pt.state = 0;
	bcm_bus->download_progress = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_download_image(&bcm_bus->download_firmware_pt, evt, 0,
				(uint8_t *)wifi_firmware_image, wifi_firmware_image_size);
	if (err != 0) return err;

	// download nvram
	bcm_bus->download_firmware_pt.state = 0;
	bcm_bus->download_progress = 0;
	vsfsm_pt_entry(pt);
	reg = (sizeof(wifi_nvram_image) + 63) & ~63;
	err = bcm_bus_download_image(&bcm_bus->download_firmware_pt, evt,
				BCM_RAM_SIZE - 4 - reg, (uint8_t *)wifi_nvram_image, reg);
	if (err != 0) return err;

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	reg = ((sizeof(wifi_nvram_image) + 63) & ~63) / 4;
	reg = (~reg << 16) | reg;
	err = bcm_bus_write_reg(&bcm_bus->download_firmware_pt, evt,
							BCM_RAM_SIZE - 4, 4, reg);
	if (err != 0) return err;

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_reset_device_core(&bcm_bus->download_firmware_pt, evt,
										BCM_CORE_ARM_BASE);
	if (err != 0) return err;

	// check arm core is up
	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_device_core_isup(&bcm_bus->download_firmware_pt, evt,
										BCM_CORE_ARM_BASE, &up);
	if (err != 0) return err;
	if (!up)
	{
		return VSFERR_FAIL;
	}

	// wait HT clock
	bcm_bus->retry = 0;
	while (1)
	{
		bcm_bus->download_firmware_pt.state = 0;
		vsfsm_pt_entry(pt);
		err = bcm_bus_read_reg(&bcm_bus->download_firmware_pt, evt,
						BCM_BPREG_CHIPCLKCSR, 1, (uint8_t *)&bcm_bus->tmpreg);
		if (err != 0) return err;

		if (bcm_bus->tmpreg & BCM_CHIPCLKCSR_HT_AVAIL)
		{
			break;
		}
		
		bcm_bus->retry++;
		if (bcm_bus->retry > 100)
			return VSFERR_FAIL;
		
		vsfsm_pt_delay(pt, 3);
	}

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->download_firmware_pt, evt,
						BCM_COREREG_SDIOD_INT_HOST_MASK, 4, BCM_HMB_SW_MASK);
	if (err != 0) return err;

#ifdef BCMWIFI_SDIO
	if (BCM_BUS_TYPE_SDIO == bcm_bus->type)
	{
		bcm_bus->download_firmware_pt.state = 0;
		vsfsm_pt_entry(pt);
		err = bcm_bus_write_reg(&bcm_bus->download_firmware_pt, evt,
						BCM_COREREG_SDIOD_FINT_MASK, 1, 2);
		if (err != 0) return err;
	}
#endif

	bcm_bus->download_firmware_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->download_firmware_pt, evt,
							BCM_BPREG_WATERMARK, 1, BCM_SPI_F2_WATERMARK);
	if (err != 0) return err;

	vsfsm_pt_end(pt);
	return VSFERR_NONE;
}

vsf_err_t bcm_bus_init(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	uint32_t reg;
	vsf_err_t err = VSFERR_NONE;

	vsfsm_pt_begin(pt);
	if (vsfsm_crit_enter(&bcm_bus->crit_init, pt->sm))
	{
		vsfsm_pt_wfe(pt, VSFSM_EVT_USER_LOCAL);
	}

	if (bcm_bus->port.mode_port != VSFHAL_DUMMY_PORT)
	{
		vsfhal_gpio_init(bcm_bus->port.mode_port);
		vsfhal_gpio_config(bcm_bus->port.mode_port, bcm_bus->port.mode_pin, VSFHAL_GPIO_OUTPP);

		if (BCM_BUS_TYPE_SDIO == bcm_bus->type)
			vsfhal_gpio_clear(bcm_bus->port.mode_port, 1 << bcm_bus->port.mode_pin);
		else
			vsfhal_gpio_set(bcm_bus->port.mode_port, 1 << bcm_bus->port.mode_pin);
	}
	vsfhal_gpio_init(bcm_bus->port.rst_port);
	vsfhal_gpio_config(bcm_bus->port.rst_port, bcm_bus->port.rst_pin,
								VSFHAL_GPIO_OUTOD);

	bcm_bus->receiving = false;
	bcm_bus->cur_backplane_base = 0;
	bcm_bus->init_pt.sm = pt->sm;
	bcm_bus->init_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus->op->init(&bcm_bus->init_pt, evt);
	if (err > 0) return err; else if (err < 0) goto end;

	// read F1 signature
	bcm_bus->init_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_read_reg(&bcm_bus->init_pt, evt, BCM_SBREG_F1SIG, 4,
							(uint8_t *)&bcm_bus->f1sig);
	if (err > 0) return err; else if (err < 0) goto end;

	// require ALP
	bcm_bus->init_pt.state = 0;
	vsfsm_pt_entry(pt);
	reg = BCM_CHIPCLKCSR_ALP_REQ;
	if (BCM_BUS_TYPE_SDIO == bcm_bus->type)
		reg |= BCM_CHIPCLKCSR_FORCE_HW_CLK_OFF | BCM_CHIPCLKCSR_FORCE_ALP;
	err = bcm_bus_write_reg(&bcm_bus->init_pt, evt, BCM_BPREG_CHIPCLKCSR,
							1, reg);
	if (err > 0) return err; else if (err < 0) goto end;

	// wait ALP
	bcm_bus->retry = 0;
	while (1)
	{
		bcm_bus->init_pt.state = 0;
		vsfsm_pt_entry(pt);
		err = bcm_bus_read_reg(&bcm_bus->init_pt, evt, BCM_BPREG_CHIPCLKCSR,
							   (BCM_BUS_TYPE_SDIO == bcm_bus->type) ? 1 : 2,
							   (uint8_t *)&bcm_bus->tmpreg);
		if (err > 0) return err; else if (err < 0) goto end;

		if (bcm_bus->tmpreg & BCM_CHIPCLKCSR_ALP_AVAIL)
		{
			break;
		}
		
		bcm_bus->retry++;
		if (bcm_bus->retry > 30)
			return VSFERR_FAIL;
		vsfsm_pt_delay(pt, 3);
	}

	// clear ALP require
	bcm_bus->init_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_write_reg(&bcm_bus->init_pt, evt, BCM_BPREG_CHIPCLKCSR,
							1, 0);
	if (err > 0) return err; else if (err < 0) goto end;

	if (bcm_bus->op->enable != NULL)
	{
		bcm_bus->init_pt.state = 0;
		vsfsm_pt_entry(pt);
		err = bcm_bus->op->enable(&bcm_bus->init_pt, evt);
		if (err > 0) return err; else if (err < 0) goto end;
	}

	// download firmware
	bcm_bus->init_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_download_firmware_nvram(&bcm_bus->init_pt, evt);
	if (err > 0) return err; else if (err < 0) goto end;

	if (bcm_bus->op->waitf2 != NULL)
	{
		bcm_bus->init_pt.state = 0;
		vsfsm_pt_entry(pt);
		err = bcm_bus->op->waitf2(&bcm_bus->init_pt, evt);
		if (err > 0) return err; else if (err < 0) goto end;
	}

	bcm_bus->is_up = 1;
end:
	vsfsm_crit_leave(&bcm_bus->crit_init);
	vsfsm_pt_end(pt);
	return err;
}

vsf_err_t bcm_bus_transact(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
			uint8_t rw, uint8_t func, uint32_t addr, uint16_t size,
			struct vsf_buffer_t *buffer)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	uint32_t base = addr & ~0x7FFF, cur_base = bcm_bus->cur_backplane_base;
	vsf_err_t err = VSFERR_NONE;

	vsfsm_pt_begin(pt);
	if (vsfsm_crit_enter(&bcm_bus->crit_buffer, pt->sm))
	{
		vsfsm_pt_wfe(pt, VSFSM_EVT_USER_LOCAL);
	}

	bcm_bus->bufacc_pt.sm = pt->sm;
	if ((BCM_FUNC_BACKPLANE == func) && (addr & ~0x1FFFF))
	{
		// write SB, set address window first
		if (bcm_bus->cur_backplane_base != base)
		{
			// backplane core registers, set backplane window first
			if ((base & 0x7F000000) != (cur_base & 0x7F000000))
			{
				// the MSB of base is used to indicate backplane
				bcm_bus->bufacc_buffer.buffer = bcm_bus->bufacc_mem;
				*(uint32_t *)bcm_bus->bufacc_buffer.buffer =\
							bcm_bus->op->fix_u32(bcm_bus, (base >> 24) & 0x7F);
				bcm_bus->bufacc_pt.state = 0;
				vsfsm_pt_entry(pt);
				err = bcm_bus->op->transact(&bcm_bus->bufacc_pt, evt,
						BCM_CMD_WRITE, BCM_FUNC_BACKPLANE, BCM_BPREG_SBADDR + 2,
						1, &bcm_bus->bufacc_buffer);
				if (err > 0) return err; else if (err < 0) goto end;
			}
			if ((base & 0x00FF0000) != (cur_base & 0x00FF0000))
			{
				bcm_bus->bufacc_buffer.buffer = bcm_bus->bufacc_mem;
				*(uint32_t *)bcm_bus->bufacc_buffer.buffer =\
							bcm_bus->op->fix_u32(bcm_bus, base >> 16);
				bcm_bus->bufacc_pt.state = 0;
				vsfsm_pt_entry(pt);
				err = bcm_bus->op->transact(&bcm_bus->bufacc_pt, evt,
						BCM_CMD_WRITE, BCM_FUNC_BACKPLANE, BCM_BPREG_SBADDR + 1,
						1, &bcm_bus->bufacc_buffer);
				if (err > 0) return err; else if (err < 0) goto end;
			}
			if ((base & 0x0000FF00) != (cur_base & 0x0000FF00))
			{
				bcm_bus->bufacc_buffer.buffer = bcm_bus->bufacc_mem;
				*(uint32_t *)bcm_bus->bufacc_buffer.buffer =\
							bcm_bus->op->fix_u32(bcm_bus, base >> 8);
				bcm_bus->bufacc_pt.state = 0;
				vsfsm_pt_entry(pt);
				err = bcm_bus->op->transact(&bcm_bus->bufacc_pt, evt,
						BCM_CMD_WRITE, BCM_FUNC_BACKPLANE, BCM_BPREG_SBADDR + 0,
						1, &bcm_bus->bufacc_buffer);
				if (err > 0) return err; else if (err < 0) goto end;
			}

			bcm_bus->cur_backplane_base = base;
		}
	}

	bcm_bus->bufacc_pt.state = 0;
	vsfsm_pt_entry(pt);
	if ((BCM_FUNC_BACKPLANE == func) && (addr & ~0x1FFFF))
	{
		addr &= 0x7FFF;
	}
	err = bcm_bus->op->transact(&bcm_bus->bufacc_pt, evt, rw, func, addr, size,
								buffer);
	if (err > 0) return err; else if (err < 0) goto end;

end:
	vsfsm_crit_leave(&bcm_bus->crit_buffer);
	vsfsm_pt_end(pt);
	return err;
}

vsf_err_t bcm_bus_read_reg(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
							uint32_t addr, uint8_t len, uint8_t *value)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err = VSFERR_NONE;

	vsfsm_pt_begin(pt);
	if (len > 4)
	{
		return VSFERR_INVALID_PARAMETER;
	}

	if (vsfsm_crit_enter(&bcm_bus->crit_reg, pt->sm))
	{
		vsfsm_pt_wfe(pt, VSFSM_EVT_USER_LOCAL);
	}

	bcm_bus->regacc_buffer.buffer = bcm_bus->regacc_mem;
	bcm_bus->regacc_pt.sm = pt->sm;
	bcm_bus->regacc_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_transact(&bcm_bus->regacc_pt, evt, BCM_CMD_READ,
						(addr & ~0x7FF ? BCM_FUNC_BACKPLANE : BCM_FUNC_BUS),
						addr, len, &bcm_bus->regacc_buffer);
	if (err > 0) return err; else if (err < 0) goto end;

	memcpy(value, bcm_bus->regacc_buffer.buffer, len);

end:
	vsfsm_crit_leave(&bcm_bus->crit_reg);
	vsfsm_pt_end(pt);
	return err;
}

vsf_err_t bcm_bus_write_reg(struct vsfsm_pt_t *pt, vsfsm_evt_t evt,
							uint32_t addr, uint8_t len, uint32_t value)
{
	struct bcm_bus_t *bcm_bus = (struct bcm_bus_t *)pt->user_data;
	vsf_err_t err = VSFERR_NONE;

	vsfsm_pt_begin(pt);
	if (len > 4)
	{
		return VSFERR_INVALID_PARAMETER;
	}

	if (vsfsm_crit_enter(&bcm_bus->crit_reg, pt->sm))
	{
		vsfsm_pt_wfe(pt, VSFSM_EVT_USER_LOCAL);
	}

	bcm_bus->regacc_buffer.buffer = bcm_bus->regacc_mem;
	*(uint32_t *)bcm_bus->regacc_buffer.buffer =\
					bcm_bus->op->fix_u32(bcm_bus, value);
	bcm_bus->regacc_pt.sm = pt->sm;
	bcm_bus->regacc_pt.state = 0;
	vsfsm_pt_entry(pt);
	err = bcm_bus_transact(&bcm_bus->regacc_pt, evt, BCM_CMD_WRITE,
						(addr & ~0x7FF ? BCM_FUNC_BACKPLANE : BCM_FUNC_BUS),
						addr, len, &bcm_bus->regacc_buffer);
	if (err > 0) return err; else if (err < 0) goto end;

end:
	vsfsm_crit_leave(&bcm_bus->crit_reg);
	vsfsm_pt_end(pt);
	return err;
}

#endif
